package com.hilin.common.util;

import lombok.extern.slf4j.Slf4j;

/**
 * 本id工具使用了雪花算法,算法为：1+41位毫秒时间戳+10位机器码+12位序列号,及64位整数（无符号Long类型）
 * 本工具修改为：8位机器码，14位序列号
 * 使用时间为690年，集群内可以使用256台服务器，每1毫秒每台服务器可生成16384个不重复id
 * 经测试，单个线程100万个随机数耗时119毫秒，处理器i7 9750h,系统Windows10_1809_17763.1697(专业版)
 * <br>
 * 后期考虑id生成器交由认证平台统一生成
 */
@Slf4j
public class IdUtils {

    /**
     * 机器编号，如果不指定，则设置为ip的最后一位，如果ip获取失败，则设置为0
     */
    private static final long MATH_ID;

    static {
        int[] ipArr = IpUtils.getLocalIpArr();
        MATH_ID = (ipArr == null || ipArr.length < 4) ? 0 : ipArr[3];
    }

    /**
     * 默认实例，默认会使用ip最后一位当作计器编号，如果ip获取失败，则机器编码为0
     */
    public static final IdUtils DEFAULT = new IdUtils(MATH_ID);

    /**
     * 机器码长度
     */
    private final static int LEN_MACH = 8;

    /**
     * 序列号长度
     */
    private final static int LEN_SN = 14;

    /**
     * 机器码
     */
    private final long machId;
    /**
     * 最后一次访问时间
     */
    private long lastTime = System.currentTimeMillis();

    /**
     * 当前的id
     */
    private long currentSn = 0;

    /**
     * 机器码
     *
     * @param machId
     */
    public IdUtils(long machId) {
        this.machId = machId;
    }

    /**
     * 生成id
     *
     * @return
     */
    public synchronized Long gen() {
        // 判断是否超过当前限制的速率
        if (currentSn >= ((1 << LEN_SN) - 1)) {
            throw new ReturnException("id生成超过最大速率");
        }
        long currentTime = System.currentTimeMillis();
        if (lastTime == currentTime) {
            currentSn = currentSn + 1;
        } else {
            lastTime = currentTime;
            currentSn = 0;
        }

        return ((currentTime << (LEN_MACH + LEN_SN)) | (machId << LEN_SN) | currentSn);
    }

}
